#include "FKFilesList.h"

#include <QDir>
#include <QMap>

#include "FKFileInfo.h"

#include "FKLogger.h"

int FKFilesList::sId=FKFilesList::declareFKMetaType();
int FKFilesList::declareFKMetaType(){
    qRegisterMetaTypeStreamOperators<FKFilesList>("FKFilesList");
    return qRegisterMetaType<FKFilesList>();
}

FKFilesList::FKFilesList():_size(0){}

FKFilesList::FKFilesList(const FKFilesList& other):
//	_rootPath(other._rootPath),
    _size(other._size),
    _paths(other._paths),
    _names(other._names),
    _versions(other._versions),
    _weights(other._weights),
    _osTypes(other._osTypes){
}

FKFilesList::FKFilesList(const QList<FKFileInfo> &list):_size(0){
    foreach(FKFileInfo file, list){
        addFile(file.name,file.version,file.relativePath+file.fileName,file.size,file.OStype);
//        FK_MLOGV("creating flist from f list",file.size)
    }
}

bool FKFilesList::saveToFile(QString path){
    QFile file(path);
    if(file.open(QIODevice::WriteOnly)){
        QDataStream stream(&file);
        stream.setVersion(QDataStream::Qt_5_0);
        stream<< (*this);
        return true;
    }
    return false;
}

FKFilesList FKFilesList::loadFromFile(QString path){
    QFile file(path);
    FKFilesList fl;
    if(file.open(QIODevice::ReadOnly)){
        QDataStream stream(&file);
        stream.setVersion(QDataStream::Qt_5_0);
        stream>>fl;
    }
    return fl;
}

FKFilesList FKFilesList::loadFromPlatformFolders(QDir appFolder){
    FKFilesList fl;
    QStringList lst;
    lst<<(FKPaths::binSubfolderWin+FKPaths::infoListFile)
       <<(FKPaths::binSubfolderMac+FKPaths::infoListFile)
       <<(FKPaths::binSubfolderNix+FKPaths::infoListFile);
    foreach(QString p,lst){
        QFileInfo f(appFolder,p);
        fl+=loadFromFile(f.absoluteFilePath());
    }
    return fl;
}

FKFilesList::~FKFilesList(){}

int FKFilesList::size() const{return _size;}
QStringList FKFilesList::paths() const{return _paths;}
QStringList FKFilesList::names() const{return _names;}
QStringList FKFilesList::versions() const{return _versions;}
QList<qint64> FKFilesList::weights() const{return _weights;}
QList<quint8> FKFilesList::osTypes() const{return _osTypes;}

QString FKFilesList::path(int pos) const{
    if(pos>=_size || pos<0)return QString();
    return _paths.at(pos);
}

QString FKFilesList::name(int pos) const{
    if(pos>=_size || pos<0)return QString();
    return _names.at(pos);
}

QString FKFilesList::version(int pos) const{
    if(pos>=_size || pos<0)return QString();
    return _versions.at(pos);
}

qint64 FKFilesList::weight(int pos) const{
    if(pos>=_size || pos<0)return 0;
    return _weights.at(pos);
}

quint8 FKFilesList::osType(int pos) const{
    if(pos>=_size || pos<0)return 0;
    return _osTypes.at(pos);
}

//void FKFilesList::setRootPath(QString rootPath){
//	_rootPath=rootPath;
//}

int FKFilesList::find(QString fl,quint8 os) const{
    int index=_names.indexOf(fl);
    while(index>=0){
        if(_osTypes.at(index) & os)return index;
        index=_names.indexOf(fl,index+1);
    }
    return -1;
}

QMap<QString,QString> FKFilesList::fileMap(/*QDir root*/){
    QMap<QString,QString> m;
    for(int i=0;i<_size;++i){
//        QFileInfo inf(root,_paths.at(i));
        m.insert(QString::number(_osTypes.at(i))+_names.at(i),_paths.at(i)/*inf.canonicalFilePath()*/);
    }
    return m;
}

QMap<QString,QString> FKFilesList::fileMapAbsolute(QDir root){
    QMap<QString,QString> m;
    for(int i=0;i<_size;++i){
        QFileInfo inf(root,_paths.at(i));
        m.insert(QString::number(_osTypes.at(i))+_names.at(i),inf.canonicalFilePath());
    }
    return m;
}

void FKFilesList::refreshWeights(QDir &root){
    for(int i=0;i<_size;++i){
        QFileInfo inf(root,_paths.at(i));
//        FK_MLOGV("file path founded",inf.absoluteFilePath())
        _weights[i]=inf.size();
    }
}

void FKFilesList::addFile(QString name, QString version, QString relativePath, qint64 weight, quint8 osType){
    ++_size;
    _names.append(name);
    _versions.append(version);
    _paths.append(relativePath);
    _weights.append(weight);
    _osTypes.append(osType);
//    return this;
}

FKFilesList FKFilesList::compare(const FKFilesList& other) const{
    FKFilesList c;
    for(int i=0;i<_size;++i)c.addFile(_names.at(i),QString(),QString(),0,_osTypes.at(i));
    QString curName;
    quint8 osType;
    int index;
    for(int i=0;i<other._size;++i){
        curName=other._names.at(i);
        osType=other._osTypes.at(i);
        if(c._names.contains(curName)){
            index=c._names.indexOf(curName);
            bool success=false;
            do{
                if(osType==c._osTypes.at(index)){
                    c._paths[index]=other._paths.at(i);
                    c._versions[index]=other._versions.at(i);
                    c._weights[index]=other._weights.at(i);
                    success=true;
                    break;
                }
            }while((index=c._names.indexOf(curName,index+1))>=0);
            if(!success){
                c.addFile(other._names.at(i), other._versions.at(i), other._paths.at(i), other._weights.at(i), other._osTypes.at(i));
            }else{
                success=false;
            }
        }else{
            c.addFile(other._names.at(i), other._versions.at(i), other._paths.at(i), other._weights.at(i), other._osTypes.at(i));
        }
    }
//    FK_MLOGV("File list size",_size)
    return c;
}

QDataStream& operator<<(QDataStream& stream, const FKFilesList& lst){
    stream<<lst._names;
    stream<<lst._versions;
    stream<<lst._paths;
    stream<<lst._weights;
    stream<<lst._osTypes;
    return stream;
}

QDataStream& operator>>(QDataStream& stream, FKFilesList& lst){
    stream>>lst._names;
    stream>>lst._versions;
    stream>>lst._paths;
    stream>>lst._weights;
    stream>>lst._osTypes;
    lst._size=lst._versions.size();
    return stream;
}

bool FKFilesList::operator>=(const FKFilesList &other) const{
    FKFilesList first(*this);
    FKFilesList second(other);
    diff(first,second);
    return !(second.size());
}

bool FKFilesList::operator<=(const FKFilesList &other) const{
    return other>=(*this);
}

void FKFilesList::diff(FKFilesList &first, FKFilesList &second){
    compare(first,second);
    FKFilesList left, right;
    int s=first.size();
    for(int i=0;i<s;++i){
        if(first._versions.at(i)!=second._versions.at(i)/* || first._weights.at(i)!=second._weights.at(i)*/){
            if(first._versions.at(i)!=QString())left.addFile(i,first);
            if(second._versions.at(i)!=QString())right.addFile(i,second);
        }
    }
    first=left;
    second=right;
}

void FKFilesList::addFile(int i, const FKFilesList &source){
    addFile(source.name(i), source.version(i), source.path(i), source.weight(i), source.osType(i));
}

void FKFilesList::compare(FKFilesList &first, FKFilesList &second){
    second=first.compare(second);
    first=second.compare(first);
}

FKFilesList FKFilesList::filter(quint8 osType) const{
    FKFilesList f;
    for(int i=0;i<_size;++i){
        if(_osTypes.at(i) & osType)f.addFile(i,*this);
    }
    return f;
}

void FKFilesList::operator+=(const FKFilesList &other){
    _names.append(other._names);
    _versions.append(other._versions);
    _paths.append(other._paths);
    _weights.append(other._weights);
    _osTypes.append(other._osTypes);
    _size=_versions.size();
}
